/******************************************************************************
This file is part of AppKit.
Project: appkit
Author : FergusZeng
Email  : cblock@126.com
git	   : https://gitee.com/newgolo/appkit.git
*******************************************************************************
MIT License

Copyright (c) 2022 cblock@126.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#include "appkit/packet.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "appkit/fileio.h"
#include "appkit/strutil.h"
#include "appkit/system.h"
#include "appkit/tracer.h"

/* Rotate a 32 bit integer by n bytes */
#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
#define FRAME_FLAG '\xA5'
namespace appkit {
static const int kPacketSizeMin = 4;
static const int kPacketSizeMax = 518;

std::string AnyPacket::Frame::decode(const std::string& packet) {
    std::string payload;
    int size = packet.size();
    if (size < kPacketSizeMin || size > kPacketSizeMax) {
        TRACE_ERR_CLASS("packet size error, size: %d", size);
        return payload;
    }
    if (packet[0] != FRAME_FLAG || packet[size - 1] != FRAME_FLAG) {
        TRACE_ERR_CLASS("packet head error!");
        return payload;
    }
    std::string frame;
    for (auto i = 1; i < size - 1; ++i) {
        if (packet[i] == '\xA0' && packet[i + 1] == '\x05') {
            frame.append("\xA5");
            i++;
        } else if (packet[i] == '\xA0' && packet[i + 1] == '\x0A') {
            frame.append("\xA0");
            i++;
        } else {
            frame.append(1, packet[i]);
        }
    }
    auto len = frame.size();
    auto calcrc = CRCCheck::check16CRC8005(frame.substr(0, len - 2));
    uint16 pktcrc = frame[len - 1];
    pktcrc = (pktcrc << 8) + frame[len - 2];
    if (calcrc != pktcrc) {
        TRACE_ERR_CLASS("crc error, calcrc = 0X%04X pktcrc = 0X%04X", calcrc,
                        pktcrc);
        return payload;
    }
    uint8 payloadLen = frame[0];
    if (payloadLen != len - 3) {
        TRACE_ERR_CLASS("payload size error, %d - %d", payloadLen, len - 3);
        return payload;
    }
    return frame.substr(1, payloadLen);
}

std::string AnyPacket::Frame::encode(const std::string& frame) {
    std::string buffer;
    buffer.append(1, frame.size());
    buffer.append(frame);
    uint16 crc = CRCCheck::check16CRC8005(buffer);
    buffer.append(std::string(reinterpret_cast<char*>(&crc), 2));
    std::string packet;
    packet.append(1, FRAME_FLAG);
    for (auto i = 0; i < buffer.size(); i++) {
        if (buffer[i] == '\xA5') {
            packet.append("\xA0\x05");
        } else if (buffer[i] == '\xA0') {
            packet.append("\xA0\x0A");
        } else {
            packet.append(1, buffer[i]);
        }
    }
    packet.append(1, FRAME_FLAG);
    return packet;
}

std::vector<std::string> AnyPacket::parse(const std::string& data) {
    std::vector<std::string> packets;
    if (m_buffer.size() > 8192) {
        TRACE_ERR_CLASS("buffer is full(>8192), clear it!");
        m_buffer.clear();
    }
    m_buffer.append(data);
    auto startPos = m_buffer.find("\xA5");
    if (startPos == std::string::npos) {
        TRACE_WARN_CLASS("dirty data:[%s]",
                         StrUtil::hexVisualize(m_buffer).data());
        m_buffer.clear();
        return packets;
    }
    if (startPos != 0) {
        TRACE_WARN_CLASS(
            "dirty data:[%s]",
            StrUtil::hexVisualize(m_buffer.substr(0, startPos)).data());
    }
    m_buffer.erase(0, startPos);
    while (m_buffer.size() > 0) {
        auto endPos = m_buffer.find("\xA5", startPos + 1);
        if (endPos == std::string::npos) {
            break;
        }

        if ((endPos - startPos + 1) >= kPacketSizeMin) {
            auto packet = m_buffer.substr(startPos, endPos - startPos + 1);
            packets.push_back(packet);
            m_buffer.erase(0, endPos + 1);
        } else {
            TRACE_WARN_CLASS(
                "broken data: [%s]",
                StrUtil::hexVisualize(m_buffer.substr(0, endPos)).data());
            m_buffer.erase(0, endPos);
        }

        if (m_buffer.size() == 0) {
            break;
        }
        startPos = m_buffer.find("\xA5");
        if (startPos == std::string::npos) {
            TRACE_ERR_CLASS("wild data: [%s]",
                            StrUtil::hexVisualize(m_buffer).data());
            m_buffer.clear();
            break;
        }
    }
    return packets;
}

/**
 * Polynomial:0x8005
 * Initial Value:0xffff
 * Final Xor Value:0x0
 * Input & Result Reflected:True
 */
const uint16 CRC16Table_8005[256] = {
    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601,
    0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0,
    0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81,
    0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941,
    0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01,
    0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0,
    0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081,
    0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
    0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00,
    0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0,
    0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981,
    0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41,
    0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700,
    0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0,
    0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281,
    0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
    0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01,
    0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1,
    0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80,
    0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541,
    0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101,
    0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0,
    0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481,
    0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
    0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801,
    0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1,
    0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581,
    0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341,
    0x4100, 0x81c1, 0x8081, 0x4040};

uint16 CRCCheck::check16CRC8005(const std::string& content,
                                const uint16& initVal) {
#if 1
    uint16 crc = initVal;
    for (auto& c : content) {
        crc = CRC16Table_8005[(crc ^ (c)) & 0xff] ^ (crc >> 8);
    }
    return crc ^ 0x0;
#else
    uint16 crcSum = 0xFFFF;
    for (auto i = 0; i < content.size(); i++) {
        crcSum ^= (uint8)content[i];
        for (auto j = 8; j > 0; j--) {
            if (crcSum & 0x0001) {
                crcSum >>= 1;
                crcSum ^= 0xA001;
            } else {
                crcSum >>= 1;
            }
        }
    }
    return crcSum;
#endif
}

/**
 * @struct MD5Context
 * @brief MD5上下文
 */
struct MD5Context {
    uint32 A, B, C, D; /* chaining variables */
    uint32 nblocks;
    uint8 buf[64];
    sint32 count;
};

MD5Check::MD5Check() {}

MD5Check::~MD5Check() {}

bool MD5Check::checkFile(const std::string& fileName, std::string* md5sum) {
    MD5Context ctx;
    initContext(&ctx);
    File file;
    uint8 buffer[4096];
    if (!file.open(fileName, IO_MODE_RD_ONLY)) {
        return false;
    }
    while (1) {
        int n = file.readData(reinterpret_cast<char*>(buffer), sizeof(buffer));
        if (n < 0) {
            return false;
        } else if (0 == n) {
            break;
        } else {
            md5Write(&ctx, buffer, n);
        }
    }
    md5Final(&ctx);
    file.close();
    *md5sum = "";
    for (int i = 0; i < 16; i++) {
        char tmp[8] = {0};
        snprintf(tmp, sizeof(tmp) - 1, "%02x", ctx.buf[i]);
        md5sum->append(tmp);
    }
    return true;
}

bool MD5Check::checkString(const std::string& srcString, std::string* md5sum) {
    MD5Context ctx;
    initContext(&ctx);
    char* buf = const_cast<char*>(CSTR(srcString));
    if (srcString.empty()) {
        return false;
    }
    md5Write(&ctx, reinterpret_cast<uint8*>(buf), srcString.size());
    md5Final(&ctx);
    md5sum->clear();
    for (int i = 0; i < 16; i++) {
        char tmp[8] = {0};
        snprintf(tmp, sizeof(tmp) - 1, "%02x", ctx.buf[i]);
        md5sum->append(tmp);
    }
    return true;
}

void MD5Check::initContext(MD5Context* ctx) {
    ctx->A = 0x67452301;
    ctx->B = 0xefcdab89;
    ctx->C = 0x98badcfe;
    ctx->D = 0x10325476;
    ctx->nblocks = 0;
    ctx->count = 0;
}

/* transform n*64 bytes */
void MD5Check::calculate(MD5Context* ctx, uint8* data) {
    uint32 correct_words[16];
    uint32 A = ctx->A;
    uint32 B = ctx->B;
    uint32 C = ctx->C;
    uint32 D = ctx->D;
    uint32* cwp = correct_words;

    if (Endian::isBigEndian()) {
        int i;
        uint8 *p2, *p1;
        for (i = 0, p1 = data, p2 = reinterpret_cast<uint8*>(correct_words);
             i < 16; i++, p2 += 4) {
            p2[3] = *p1++;
            p2[2] = *p1++;
            p2[1] = *p1++;
            p2[0] = *p1++;
        }
    } else {
        memcpy(correct_words, data, 64);
    }

/* These are the four functions used in the four steps of the MD5 algorithm
   and defined in the RFC 1321.  The first function is a little bit optimized
   (as found in Colin Plumbs public domain implementation).  */
/* #define FF(b, c, d) ((b & c) | (~b & d)) */
#define FF(b, c, d) (d ^ (b & (c ^ d)))
#define FG(b, c, d) FF(d, b, c)
#define FH(b, c, d) (b ^ c ^ d)
#define FI(b, c, d) (c ^ (b | ~d))

#define OP(a, b, c, d, s, T)             \
    do {                                 \
        a += FF(b, c, d) + (*cwp++) + T; \
        a = rol(a, s);                   \
        a += b;                          \
    } while (0)

    /* Before we start, one word about the strange constants.
       They are defined in RFC 1321 as

       T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
     */

    /* Round 1.  */
    OP(A, B, C, D, 7, 0xd76aa478);
    OP(D, A, B, C, 12, 0xe8c7b756);
    OP(C, D, A, B, 17, 0x242070db);
    OP(B, C, D, A, 22, 0xc1bdceee);
    OP(A, B, C, D, 7, 0xf57c0faf);
    OP(D, A, B, C, 12, 0x4787c62a);
    OP(C, D, A, B, 17, 0xa8304613);
    OP(B, C, D, A, 22, 0xfd469501);
    OP(A, B, C, D, 7, 0x698098d8);
    OP(D, A, B, C, 12, 0x8b44f7af);
    OP(C, D, A, B, 17, 0xffff5bb1);
    OP(B, C, D, A, 22, 0x895cd7be);
    OP(A, B, C, D, 7, 0x6b901122);
    OP(D, A, B, C, 12, 0xfd987193);
    OP(C, D, A, B, 17, 0xa679438e);
    OP(B, C, D, A, 22, 0x49b40821);

#undef OP
#define OP(f, a, b, c, d, k, s, T)              \
    do {                                        \
        a += f(b, c, d) + correct_words[k] + T; \
        a = rol(a, s);                          \
        a += b;                                 \
    } while (0)

    /* Round 2.  */
    OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
    OP(FG, D, A, B, C, 6, 9, 0xc040b340);
    OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
    OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
    OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
    OP(FG, D, A, B, C, 10, 9, 0x02441453);
    OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
    OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
    OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
    OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
    OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
    OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
    OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
    OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
    OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
    OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);

    /* Round 3.  */
    OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
    OP(FH, D, A, B, C, 8, 11, 0x8771f681);
    OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
    OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
    OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
    OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
    OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
    OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
    OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
    OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
    OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
    OP(FH, B, C, D, A, 6, 23, 0x04881d05);
    OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
    OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
    OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
    OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);

    /* Round 4.  */
    OP(FI, A, B, C, D, 0, 6, 0xf4292244);
    OP(FI, D, A, B, C, 7, 10, 0x432aff97);
    OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
    OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
    OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
    OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
    OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
    OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
    OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
    OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
    OP(FI, C, D, A, B, 6, 15, 0xa3014314);
    OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
    OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
    OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
    OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
    OP(FI, B, C, D, A, 9, 21, 0xeb86d391);

    /* Put checksum in context given as argument.  */
    ctx->A += A;
    ctx->B += B;
    ctx->C += C;
    ctx->D += D;
}

/* The routine updates the message-digest context to
 * account for the presence of each of the characters inBuf[0..inLen-1]
 * in the message whose digest is being computed.
 */
void MD5Check::md5Write(MD5Context* hd, uint8* inbuf, int inlen) {
    if (hd->count == 64) { /* flush the buffer */
        calculate(hd, hd->buf);
        hd->count = 0;
        hd->nblocks++;
    }
    if (!inbuf) {
        return;
    }
    if (hd->count) {
        for (; inlen && hd->count < 64; inlen--) {
            hd->buf[hd->count++] = *inbuf++;
        }
        md5Write(hd, nullptr, 0);
        if (!inlen) {
            return;
        }
    }

    while (inlen >= 64) {
        calculate(hd, inbuf);
        hd->count = 0;
        hd->nblocks++;
        inlen -= 64;
        inbuf += 64;
    }
    for (; inlen && (hd->count < 64); inlen--) {
        hd->buf[hd->count++] = *inbuf++;
    }
}

/* The routine final terminates the message-digest computation and
 * ends with the desired message digest in mdContext->digest[0...15].
 * The handle is prepared for a new MD5 cycle.
 * Returns 16 bytes representing the digest.
 */
void MD5Check::md5Final(MD5Context* hd) {
    uint32 t, msb, lsb;
    uint8* p;

    md5Write(hd, nullptr, 0); /* flush */

    t = hd->nblocks;
    /* multiply by 64 to make a byte count */
    lsb = t << 6;
    msb = t >> 26;
    /* add the count */
    t = lsb;
    if ((lsb += hd->count) < t) {
        msb++;
    }
    /* multiply by 8 to make a bit count */
    t = lsb;
    lsb <<= 3;
    msb <<= 3;
    msb |= t >> 29;

    if (hd->count < 56) {            /* enough room */
        hd->buf[hd->count++] = 0x80; /* pad */
        while (hd->count < 56) {
            hd->buf[hd->count++] = 0; /* pad */
        }
    } else {                         /* need one extra block */
        hd->buf[hd->count++] = 0x80; /* pad character */
        while (hd->count < 64) {
            hd->buf[hd->count++] = 0;
        }
        md5Write(hd, nullptr, 0); /* flush */
        memset(hd->buf, 0, 56);   /* fill next block with zeroes */
    }
    /* append the 64 bit count */
    hd->buf[56] = lsb;
    hd->buf[57] = lsb >> 8;
    hd->buf[58] = lsb >> 16;
    hd->buf[59] = lsb >> 24;
    hd->buf[60] = msb;
    hd->buf[61] = msb >> 8;
    hd->buf[62] = msb >> 16;
    hd->buf[63] = msb >> 24;
    calculate(hd, hd->buf);

    p = hd->buf;
    if (Endian::isBigEndian()) {
        *p++ = hd->A;
        *p++ = hd->A >> 8;
        *p++ = hd->A >> 16;
        *p++ = hd->A >> 24;
        *p++ = hd->B;
        *p++ = hd->B >> 8;
        *p++ = hd->B >> 16;
        *p++ = hd->B >> 24;
        *p++ = hd->C;
        *p++ = hd->C >> 8;
        *p++ = hd->C >> 16;
        *p++ = hd->C >> 24;
        *p++ = hd->D;
        *p++ = hd->D >> 8;
        *p++ = hd->D >> 16;
        *p++ = hd->D >> 24;
    } else {
        *(reinterpret_cast<uint32*>(p)) = hd->A;
        p += 4;
        *(reinterpret_cast<uint32*>(p)) = hd->B;
        p += 4;
        *(reinterpret_cast<uint32*>(p)) = hd->C;
        p += 4;
        *(reinterpret_cast<uint32*>(p)) = hd->D;
        p += 4;
    }
}

/* base64编码表 */
static const char* s_base64Table =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

char Base64::findIndex(const char* str, char c) {
    const char* p = strchr(str, c);
    if (!p) {
        return -1;
    }
    return static_cast<char>(p - str);
}

bool Base64::encode(const std::string& binDataIn, std::string* b64DataOut) {
    b64DataOut->clear();
    const char* base64Table = s_base64Table;
    uint32 srcLen = binDataIn.size();
    if (srcLen == 0) {
        return false;
    }
    uint32 i, j;
    for (i = 0, j = 0; i < srcLen / 3; i++) {
        b64DataOut->append(CH2STR(
            base64Table[(binDataIn[j] >> 2) & 0x3f])); /* 取第一字符前6bit */
        b64DataOut->append(CH2STR(
            base64Table[((binDataIn[j] << 4) & 0x30) |
                        ((binDataIn[j + 1] >> 4) &
                         0x0f)]));  // 第一字符的后2bit与第二字符的前4位进行合并
        b64DataOut->append(CH2STR(
            base64Table
                [((binDataIn[j + 1] << 2) & 0x3c) |
                 ((binDataIn[j + 2] >> 6) &
                  0x03)]));  // 将第二字符的后4bit与第三字符的前2bit组合并
        b64DataOut->append(CH2STR(
            base64Table[binDataIn[j + 2] & 0x3f])); /* 取第三字符的后6bit */
        j += 3;
    }

    /* 非3的整数倍补“=” */
    if ((srcLen % 3) == 1) {
        b64DataOut->append(CH2STR(base64Table[(binDataIn[j] >> 2) & 0x3f]));
        b64DataOut->append(
            CH2STR(base64Table[((binDataIn[j] << 4) & 0x30) |
                               ((binDataIn[j + 1] >> 4) & 0x0f)]));
        b64DataOut->append("=");
        b64DataOut->append("=");
    } else if ((srcLen % 3) == 2) {
        b64DataOut->append(CH2STR(base64Table[(binDataIn[j] >> 2) & 0x3f]));
        b64DataOut->append(
            CH2STR(base64Table[((binDataIn[j] << 4) & 0x30) |
                               ((binDataIn[j + 1] >> 4) & 0x0f)]));
        b64DataOut->append(CH2STR(base64Table[(binDataIn[j + 1] << 2) & 0x3c]));
        b64DataOut->append("=");
    }
    return true;
}
bool Base64::decode(const std::string& b64DataIn, std::string* binDataOut) {
    binDataOut->clear();
    const char* base64Table = s_base64Table;
    uint32 srcLen = b64DataIn.size();
    if (srcLen % 4 != 0) {
        return false;
    }
    uint32 i = 0;
    char buf[4] = {0};
    // uint32 destLen = (srcLen>>2)*3-2;
    for (i = 0; i < srcLen; i += 4) {
        /* 四个码译成三个字符 */
        buf[0] = findIndex(base64Table, b64DataIn[i]);
        buf[1] = findIndex(base64Table, b64DataIn[i + 1]);

        binDataOut->append(
            CH2STR(((buf[0] << 2) & 0xfc) | ((buf[1] >> 4) & 0x03)));
        if (b64DataIn[i + 2] == '=') {
            break;
        }
        buf[2] = findIndex(base64Table, b64DataIn[i + 2]);

        binDataOut->append(
            CH2STR(((buf[1] << 4) & 0xf0) | ((buf[2] >> 2) & 0x0f)));
        if (b64DataIn[i + 3] == '=') {
            break;
        }
        buf[3] = findIndex(base64Table, b64DataIn[i + 3]);

        binDataOut->append(CH2STR(((buf[2] << 6) & 0xc0) | (buf[3] & 0x3f)));
    }
    return true;
}

/* "+","/"和"="在url中会被转码,需要替换为"-","_"和"" */
bool Base64::encodeUrlSafe(const std::string& binDataIn,
                           std::string* b64DataOut) {
    if (encode(binDataIn, b64DataOut)) {
        *b64DataOut = StrUtil::replaceString(*b64DataOut, "+", "-");
        *b64DataOut = StrUtil::replaceString(*b64DataOut, "/", "_");
        uint32 idx = b64DataOut->size() - 1;
        for (uint32 i = idx; i >= 0; i--) {
            if (b64DataOut->at(i) != '=')
                break;
            else
                *b64DataOut = b64DataOut->erase(i, 1);
        }
        return true;
    }
    return false;
}

bool Base64::decodeUrlSafe(const std::string& b64DataIn,
                           std::string* binDataOut) {
    std::string b64Data = b64DataIn;
    b64Data = StrUtil::replaceString(b64Data, "-", "+");
    b64Data = StrUtil::replaceString(b64Data, "_", "/");
    uint32 mod4 = b64Data.size() % 4;
    if (mod4 != 0) {
        b64Data.append(4 - mod4, '='); /* 补足"=" */
    }
    return decode(b64Data, binDataOut);
}

char* Parser::parseNextSection(const char* source, uint32* startPos,
                               const char* endflag, uint32* sectionLen) {
    unsigned int len = 0;
    char* pstart =
        reinterpret_cast<char*>(const_cast<char*>(source + *startPos));
    char* ptr = pstart;
    while ((*ptr != *endflag) && (*ptr != 0)) {
        ptr++;
    }
    len = ptr - pstart;
    if (len == 0) {
        return nullptr;
    }
    *sectionLen = len;
    if (*ptr == 0) {
        *startPos += len;
    } else if (*ptr == *endflag) {
        *startPos = *startPos + len + 1;
    }
    return pstart;
}

bool Parser::parseNextString(const char* source, uint32* startPos,
                             const char* endflag, std::string* result) {
    unsigned int sectionLen = 0;
    char* sectionStart = nullptr;
    sectionStart = parseNextSection(source, startPos, endflag, &sectionLen);
    if (!sectionStart) {
        return false;
    }
    if (sectionLen > 0) {
        *result = std::string(sectionStart, sectionLen);
        return true;
    }
    return false;
}

bool Parser::parseNextInt(const char* source, uint32* startPos,
                          const char* endflag, sint32* result) {
    std::string str;
    if (!parseNextString(source, startPos, endflag, &str)) {
        return false;
    }
    *result = std::stoi(str);
    return true;
}

RingBuffer::RingBuffer() {}

RingBuffer::~RingBuffer() {}

bool RingBuffer::init(int capacity) {
    if (!m_bufPtr && capacity > 0) {
        m_bufPtr = std::make_unique<char[]>(capacity);
        if (m_bufPtr) {
            m_capacity = capacity;
            return true;
        }
    }
    return false;
}

void RingBuffer::clear() {
    std::lock_guard<std::mutex> lock(m_mutex);
    m_startPos = 0;
    m_endPos = 0;
    m_occupant = 0;
}

int RingBuffer::capacity() { return m_capacity; }

int RingBuffer::space() { return m_capacity - m_occupant; }

int RingBuffer::size() { return m_occupant; }

int RingBuffer::putData(char* data, int bytes) {
    if (bytes == 0) {
        return 0;
    }
    std::lock_guard<std::mutex> lock(m_mutex);
    int capacity = m_capacity;
    int bytesToWrite = MIN(bytes, capacity - m_occupant);
    if (bytesToWrite <= capacity - m_endPos) {
        memcpy(m_bufPtr.get() + m_endPos, data, bytesToWrite);
        m_endPos += bytesToWrite;
        if (m_endPos == capacity) {
            m_endPos = 0;
        }
    } else {
        int sizeP1 = capacity - m_endPos;
        int sizeP2 = bytesToWrite - sizeP1;
        memcpy(m_bufPtr.get() + m_endPos, data, sizeP1);
        memcpy(m_bufPtr.get(), data + sizeP1, sizeP2);
        m_endPos = sizeP2;
    }

    m_occupant += bytesToWrite;
    return bytesToWrite;
}

int RingBuffer::getData(char* data, int bytes) {
    if (bytes == 0) {
        return 0;
    }
    std::lock_guard<std::mutex> lock(m_mutex);
    int capacity = m_capacity;
    int bytesToRead = MIN(bytes, m_occupant);
    if (bytesToRead <= capacity - m_startPos) {
        memcpy(data, m_bufPtr.get() + m_startPos, bytesToRead);
        m_startPos += bytesToRead;
        if (m_startPos == capacity) {
            m_startPos = 0;
        }
    } else {
        int sizeP1 = capacity - m_startPos;
        int sizeP2 = bytesToRead - sizeP1;
        memcpy(data, m_bufPtr.get() + m_startPos, sizeP1);
        memcpy(data + sizeP1, m_bufPtr.get(), sizeP2);
        m_startPos = sizeP2;
    }

    m_occupant -= bytesToRead;
    return bytesToRead;
}

LineBuffer::LineBuffer() {}

LineBuffer::~LineBuffer() {
    if (m_buffer) {
        free(m_buffer);
        m_buffer = nullptr;
    }
}

bool LineBuffer::init(int capacity) {
    std::lock_guard<std::mutex> lock(m_mutex);
    m_buffer = reinterpret_cast<char*>(calloc(1, capacity));
    if (m_buffer) {
        m_curpos = m_buffer;
        m_endpos = m_buffer;
        m_capacity = capacity;
        return true;
    }
    return false;
}

void LineBuffer::clear() {
    std::lock_guard<std::mutex> lock(m_mutex);
    memset(m_buffer, 0, m_capacity);
    m_curpos = m_buffer;
    m_endpos = m_buffer;
}

int LineBuffer::capacity() { return m_capacity; }

int LineBuffer::space() {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_capacity - (m_endpos - m_curpos);
}

int LineBuffer::size() {
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_endpos - m_curpos;
}

int LineBuffer::putData(char* data, int size) {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (data && size > 0) {
        if ((m_capacity - (m_endpos - m_curpos)) <= size) {
            /* 缓冲区无法容纳新数据 */
            return 0;
        }

        int space = m_buffer + m_capacity - m_endpos;
        if (space > size) { /* 缓冲区剩余空间大于要加载的数据大小 */
            memcpy(m_endpos, data, size);
            m_endpos += size;
        } else {
            /* 把剩余数据移动到缓冲头 */
            int left = m_endpos - m_curpos;
            memmove(m_buffer, m_curpos, left);
            m_curpos = m_buffer;
            m_endpos = m_curpos + left;
            memcpy(m_endpos, data, size);
            m_endpos += size;
        }
        return size;
    }
    return 0;
}

int LineBuffer::getData(char* buf, int size) {
    std::lock_guard<std::mutex> lock(m_mutex);
    if (buf && size > 0) {
        int left = m_endpos - m_curpos;
        if (left <= 0) {
            /* 缓冲区为空 */
            return 0;
        }
        left = MIN(left, size);
        memcpy(buf, m_curpos, left);
        m_curpos += left;
        if (m_curpos == m_endpos) {
            m_curpos = m_buffer;
            m_endpos = m_buffer;
        }
        return left;
    }
    return 0;
}

int LineBuffer::find(char* str, int size) {
    char* findPos =
        StrUtil::memString(m_curpos, m_endpos - m_curpos, str, size);
    if (findPos) {
        return findPos - m_buffer;
    }
    return -1;
}

char LineBuffer::operator[](int idx) {
    char rc = m_buffer[idx];
    return rc;
}

}  // namespace appkit
